package com.project.frba.bifront.client.view;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FlexTable;
import com.gwtext.client.core.EventObject;
import com.gwtext.client.data.FieldDef;
import com.gwtext.client.data.Record;
import com.gwtext.client.data.RecordDef;
import com.gwtext.client.data.Store;
import com.gwtext.client.data.StringFieldDef;
import com.gwtext.client.widgets.Button;
import com.gwtext.client.widgets.Panel;
import com.gwtext.client.widgets.event.ButtonListenerAdapter;
import com.gwtext.client.widgets.form.ComboBox;
import com.gwtext.client.widgets.form.event.ComboBoxListenerAdapter;
import com.gwtext.client.widgets.layout.FitLayout;
import com.gwtext.client.widgets.layout.HorizontalLayout;
import com.project.frba.bifront.client.model.ColumnCounter;
import com.project.frba.bifront.client.model.Cube;
import com.project.frba.bifront.client.model.CubePosition;
import com.project.frba.bifront.client.model.DataNode;
import com.project.frba.bifront.client.model.Filter;
import com.project.frba.bifront.client.model.FrontDescriptor;
import com.project.frba.bifront.client.rpc.CubeServices;
import com.project.frba.bifront.client.rpc.CubeServicesAsync;
import com.project.frba.bifront.client.rpc.ExcelServices;
import com.project.frba.bifront.client.rpc.ExcelServicesAsync;
import com.project.frba.bifront.client.view.charts.BarChartConfigurationWindow;
import com.project.frba.bifront.client.view.charts.LineChartConfigurationWindow;
import com.project.frba.bifront.client.view.charts.PieChartConfigurationWindow;
import com.project.frba.bifront.client.view.properties.PropertySelector;

public class CubePanel extends Panel {
	
	private enum Type {TORTA, BARRA, LINEAS};
	
	private String selectedChart = null;

	private FrontDescriptor fDescriptor;
	
	private FlexTable dataGrid = new FlexTable();
	
	private Cube cube = null;
	
	private List<Filter> savedFilters = new ArrayList<Filter>();
	
	private Panel actionsPanel = new Panel();
	
	private Panel centerPanel = new Panel();
	
	private List<String> rowProps = null;
	
	private List<String> colProps = null;
	
	private List<String> notSelectedProperties = null;
	
	private ComboBox cbTypes = new ComboBox();
	
	private Button btConfigure = new Button("Configurar");
	
	private Button btFilter = new Button("Filtrar");
	
	private Button btGraphic = new Button("Graficar");
	
	private Button btExcel = new Button("Exportar a Excel");
	
	private Button btRefresh = new Button("Actualizar");
	
	public CubePanel(){
		
	}

	public CubePanel(FrontDescriptor descriptor) {		
		this.fDescriptor = descriptor;
		this.setTitle(descriptor.getLabel());
		this.setBorder(true);
		this.setPaddings(0,2,2,0);
		this.setWidth(1280);
		this.setHeight(590);
		this.setLayout(new FitLayout());
		this.setClosable(true);

		//actionsPanel
		actionsPanel.setAutoHeight(true);
		actionsPanel.setLayout(new HorizontalLayout(5));
		actionsPanel.setBorder(false);
		actionsPanel.setPaddings(3, 3, 3, 8);
		actionsPanel.setWidth(1280);
		actionsPanel.add(btConfigure);
		actionsPanel.add(btFilter);
		loadGraphicTypes();
		actionsPanel.add(cbTypes);
		actionsPanel.add(btGraphic);
		actionsPanel.add(btExcel);
		actionsPanel.add(btRefresh);
		this.add(actionsPanel);
		
		//gridPanel
		centerPanel.setHeader(false);
		centerPanel.setBorder(false);
		centerPanel.setTitle("Datos");
		centerPanel.setWidth(1260);
		centerPanel.setHeight(550);
		centerPanel.setAutoScroll(true);
		this.add(centerPanel);
		
		rowProps = fDescriptor.getRowDefaultProperties();
		colProps = fDescriptor.getColumnDefaultProperties();
		notSelectedProperties = fDescriptor.getNotDefaultProperties();
		
		loadActions();
		getCube();
	}

	private void loadGraphicTypes() {
		FieldDef[] fieldDefs = new FieldDef[]{
				new StringFieldDef("id"), 
				new StringFieldDef("name"),
				};
		RecordDef recordDef = new RecordDef(fieldDefs);
		Store store = new Store(recordDef);
		cbTypes.setStore(store);
		cbTypes.setEmptyText("Seleccione un grafico...");
		cbTypes.setDisplayField("name"); 
		cbTypes.setMode(ComboBox.LOCAL);
		cbTypes.setEditable(false);
		cbTypes.setTriggerAction(ComboBox.ALL); 
		for (Type type :Type.values()) {
			store.add(recordDef.createRecord(new Object[]{type.ordinal(), "Grafico de " + type.name()}));
		}
	}

	private void loadActions() {
		btConfigure.addListener(new ButtonListenerAdapter(){
			public void onClick(Button button, EventObject e) {
				configure();
			}
		});
		
		btFilter.addListener(new ButtonListenerAdapter(){
			public void onClick(Button button, EventObject e) {
				filter();
			}
		});
		
		btGraphic.addListener(new ButtonListenerAdapter(){
			public void onClick(Button button, EventObject e) {
				chart();
			}
		});
		
		btRefresh.addListener(new ButtonListenerAdapter(){
			public void onClick(Button button, EventObject e) {
				getCube();
			}
		});
		
		btExcel.addListener(new ButtonListenerAdapter(){
			public void onClick(Button button, EventObject e) {
				exportExcel();
			}
		});
		
		cbTypes.addListener(new ComboBoxListenerAdapter(){
			public void onSelect(ComboBox comboBox, Record record, int index) {
				selectedChart = record.getAsString("name").split(" ")[2];
			}
		});
	}

	public void setFrontDescriptor(FrontDescriptor fDescriptor) {
		this.fDescriptor = fDescriptor;
	}
	
	public FrontDescriptor getFrontDescriptor(){
		return this.fDescriptor;
	}

	public void getCube() {
		AsyncCallback<Cube> cubeCallback = new AsyncCallback<Cube>() {
			public void onSuccess(Cube resultCube) {
				cube = resultCube;
				if(cube != null){
					generateGrid();
				}
			}

			public void onFailure(Throwable caught) {
				new MessageWindow("No es posible obtener los datos: " + caught.getMessage());
			}
		};
		
		CubeServicesAsync cubeServices = (CubeServicesAsync) GWT.create(CubeServices.class);
		cubeServices.getCube(fDescriptor.getDescriptorName(), getColumnsSelectedProperties(), getRowsSelectedProperties(), getOnlyUsedFilters(), cubeCallback);
	}
	
	public List<Filter> getOnlyUsedFilters(){
		List<Filter> onlyUsedFilters = new ArrayList<Filter>();
		for (Filter filter : savedFilters) {
			String propertyName = filter.getPropertyName();
			if (colProps.contains(propertyName) || rowProps.contains(propertyName)) {
				onlyUsedFilters.add(filter);
			}
		}
		return onlyUsedFilters;
	}
	
	public void getValuesForProperty(String propertyName, AsyncCallback<List<String>> cbValues){
		CubeServicesAsync cubeServices = (CubeServicesAsync) GWT.create(CubeServices.class);
		cubeServices.getValuesForProperty(fDescriptor.getDescriptorName(), propertyName, cbValues);
	}
	
	public void cleanGrid(){
		for (int i = 0; i < dataGrid.getRowCount(); i++) {
			dataGrid.removeRow(i);
		}
		if(dataGrid.getRowCount()>0){
			cleanGrid();
		}
	}

	public String getLabelForProperty(String propertyName) {
		return this.fDescriptor.getLabelForProperty(propertyName);
	}

	public void setFilters(List<Filter> selectedFilters) {
		this.savedFilters = selectedFilters;
		
	}
	
	public void filter(){
		FilterSelector filterSelector = new FilterSelector(fDescriptor.getAllProperties(), savedFilters, this);
		filterSelector.show();
	}
	
	public void configure(){
		new PropertySelector(this);
	}
	
	public void chart(){
		if("TORTA".equals(selectedChart)){
			new PieChartConfigurationWindow(cube, fDescriptor.getDescriptorName());			
		} else if ("BARRA".equals(selectedChart)) {
			new BarChartConfigurationWindow(cube, fDescriptor.getDescriptorName());
		} else if ("LINEAS".equals(selectedChart)) {
			new LineChartConfigurationWindow(cube, fDescriptor.getDescriptorName());
		} else {
			new MessageWindow("Debe seleccionar primero un grafico.");			
		}
	}
	
	public void exportExcel(){
		AsyncCallback<String> callback = new AsyncCallback<String>(){
			public void onFailure(Throwable caught) {
				new MessageWindow("Ha ocurrido un error grave al exportar a excel.");
			}
			
			public void onSuccess(String filename) {
				new ExcelUrlWindow(filename);
			}
		};
		
		ExcelServicesAsync excelServices = (ExcelServicesAsync)GWT.create(ExcelServices.class);
		excelServices.exportToExcel(fDescriptor.getDescriptorName(), cube, callback);
		
	}
	
	public List<String> getRowsSelectedProperties(){
		return rowProps;
	}
	
	public void setRowsSelectedProperties(List<String> rowProps){
		this.rowProps = rowProps;
	}
	
	public List<String> getColumnsSelectedProperties(){
		return colProps;
	}
	
	public void setColumnsSelectedProperties(List<String> colProps){
		this.colProps = colProps;
	}
	
	public List<String> getNotSelectedProperties() {
		return notSelectedProperties;
	}
	
	public void setNotSelectedProperties(List<String> notSelectedProperties){
		this.notSelectedProperties = notSelectedProperties;
	}
	
	public void generateGrid(){
		dataGrid = new FlexTable();
		if (getRowsSelectedProperties().size() > 0) {
			int i = 0;
			for (String rowProperty : getRowsSelectedProperties()) {
				if (getColumnsSelectedProperties().size() > 0) {
					for (int j = 0; j < getColumnsSelectedProperties().size(); j++) {
						if(j == getColumnsSelectedProperties().size() - 1){
							dataGrid.setText(j, i, fDescriptor.getLabelForProperty(rowProperty));
							dataGrid.getFlexCellFormatter().getElement(j, i).setClassName("propertyNameTd");
						} else {
							dataGrid.setText(j, i, " ");
							dataGrid.getFlexCellFormatter().getElement(j, i).setClassName("propertyNameTd");
						}
					}
				} else {
					dataGrid.setText(0, i, fDescriptor.getLabelForProperty(rowProperty));
					dataGrid.getFlexCellFormatter().getElement(0, i).setClassName("propertyNameTd");
				}
				i++;
			}
			
		} else {
			for (int j = 0; j < getColumnsSelectedProperties().size(); j++) {
				dataGrid.setText(j, 0, " ");
				dataGrid.getFlexCellFormatter().getElement(j, 0).setClassName("propertyNameTd");
			}
		}
		
	
		//Setting Columns
		for (int row = 0; row < getColumnsSelectedProperties().size(); row++) {
			int col = getRowsSelectedProperties().size() == 0 ? 1 : getRowsSelectedProperties().size();
			List<DataNode> nodes = cube.getColumnTree().getNodesForLevel(row);
			for (DataNode node : nodes) {
				dataGrid.setText(row, col, node.getNodeValue());
				dataGrid.getFlexCellFormatter().getElement(row, col).setClassName("propertyTd");
				if (!node.isLeaf()) {
					dataGrid.getFlexCellFormatter().setColSpan(row, col, node.getLeafSize());
					node.isSpanned();
				} else {
					node.setRealIndex(col);
				}
				col++;
			}
			
			//Setting column property names to the header
			String colProperty = getColumnsSelectedProperties().get(row);
			Integer propertyNameCol = col;
			dataGrid.setText(row, propertyNameCol, fDescriptor.getLabelForProperty(colProperty));
			dataGrid.getFlexCellFormatter().getElement(row, propertyNameCol).setClassName("propertyNameTd");
		}

		//Setting Rows
		ColumnCounter columnCounter = new ColumnCounter(getRowsSelectedProperties().size());
		if(getRowsSelectedProperties().size() != 0){
		List<String> rootValues = cube.getValuesForProperty(rowProps.get(0));
		for (String rootValue : rootValues) {
			int firstLine = dataGrid.getRowCount();
			List<DataNode> nodes = cube.getRowTree().getNodesByDepth(rootValue);
			for (DataNode node : nodes) {
				dataGrid.setText(firstLine + node.getIndexRow(), node.getIndexCol(), node.getNodeValue());
				dataGrid.getFlexCellFormatter().getElement(firstLine + node.getIndexRow(), node.getIndexCol()).setClassName("propertyTd");
				
				if(node.isSpanneable()){
					dataGrid.getFlexCellFormatter().setRowSpan(firstLine + node.getIndexRow() ,node.getIndexCol(), node.getLeafSize());
					node.setSpanned();
				}
				
				columnCounter.addColumn(firstLine + node.getIndexRow());
				
				if (node.isLeaf()) {
					node.setRealIndex(firstLine + node.getIndexRow());
				}
			}
		}
		}
		
		//Setting Data
		for (int i = 0; i < cube.getSize(); i++) {
			CubePosition position = cube.getPosition(i, cube.getRowTree(), cube.getColumnTree());
			
			if(getRowsSelectedProperties().size() == 0){
				dataGrid.setText(getColumnsSelectedProperties().size(), 0, "Valor");
				dataGrid.getFlexCellFormatter().getElement(getColumnsSelectedProperties().size(), 0).setClassName("propertyTd");
			}
			
			if (getColumnsSelectedProperties().size() == 0) {
				dataGrid.setText(0, getRowsSelectedProperties().size(), "Valor");
				dataGrid.getFlexCellFormatter().getElement(0, getRowsSelectedProperties().size()).setClassName("propertyTd");
			}
			
			Integer col = columnCounter.getRealPosition(position.getColumnIndex(), position.getRowIndex());
			dataGrid.setText(position.getRowIndex(), col, fDescriptor.getFormatter().format(cube.getData(i)));
			dataGrid.getFlexCellFormatter().getElement(position.getRowIndex(), col).setClassName("dataTd");
		}
		
		System.out.println(dataGrid.toString());
		centerPanel.removeAll();
		centerPanel.add(dataGrid);
		dataGrid.getElement().setClassName("dataGrid");
		centerPanel.doLayout();
	}

	

}
